#pragma once

#ifdef WIN32
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include <process.h>
#else
#include <pthread.h>
#endif

#include <memory>

#include "common.hpp"

namespace base {

namespace eec {

class thread {

public:
    typedef unsigned int id;
public:
#ifdef WIN32
	thread(const EEC_STD::function<int()>& _coroutine) {
        EEC_STD::function<int()>* l_coroutine = new EEC_STD::function<int()>(_coroutine);
        vl_ = (HANDLE)_beginthreadex(NULL, 0, entry, l_coroutine, 0, &id_);
#else
	thread(const EEC_STD::function<int()>& _coroutine) {
		EEC_STD::function<int()>* l_coroutine = new EEC_STD::function<int()>(_coroutine);
        pthread_create(&vl_, NULL, entry, (void*)l_coroutine);
#endif
    }
    thread()
#ifdef WIN32
        : id_(0), vl_(NULL) {
#else
        : vl_(0) {
#endif
    }
    thread(const thread& _rhs)
#ifdef WIN32
        : id_(_rhs.id_), vl_(_rhs.vl_) {
#else
        : vl_(_rhs.vl_) {
#endif
    }

public:
    thread& operator=(const thread& _rhs) {
        if (this == &_rhs) {
            return *this;
        }
#ifdef WIN32
        id_ = _rhs.id_;
#endif
        vl_ = _rhs.vl_;
        return *this;
    }

public:
    void join() {
        
#ifdef WIN32
        if (NULL != vl_) {
            WaitForSingleObject(vl_, INFINITE);
            CloseHandle(vl_);
#else
        if (0 != vl_) {
            void* l_result = NULL;
            pthread_join(vl_, &l_result);
#endif
            vl_ = 0;
        }
    }
    void detach() {
#ifdef WIN32
        if (NULL != vl_) {
            CloseHandle(vl_);
            vl_ = NULL;
#else
        if (0 != vl_) {
            pthread_detach(vl_);
            vl_ = 0;
#endif
        }
    }
    bool joinable() {
#ifdef WIN32
        return NULL != vl_;
#else
        return 0 != vl_;
#endif
    }

public:
    id get_id() {
#ifdef WIN32
        return id_;
#else
        return (id)vl_;
#endif
    }
    static id get_current_id() {
#ifdef WIN32
        return (id)GetCurrentThreadId();
#else
        return (id)pthread_self();
#endif
    }

private:
#ifdef WIN32
    static unsigned __stdcall entry(void* _data) {
        EEC_STD::function<int()>* l_coroutine = (EEC_STD::function<int()>*)_data;
        unsigned l_result = (*l_coroutine)();
        delete l_coroutine;
        return l_result;
    }
#else
    static void* entry(void* _data) {
        EEC_STD::function<int()>* l_coroutine = (EEC_STD::function<int()>*)_data;
        unsigned int l_result = (*l_coroutine)();
        delete l_coroutine;
        return (void*)l_result;
    }
#endif

private:
#ifdef WIN32
    id id_;
    HANDLE vl_;
#else
    pthread_t vl_;
#endif
};

}   // namespace eec

}   // namespace base
